/*
 * pixiv_down - CLI-based downloading tool for https://www.pixiv.net.
 * Copyright (C) 2024  Mio
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
module app.cmds.following;

import pd.configuration;
import std.experimental.logger;

public int followingHandle(string[] args, const ref Config config)
{
   import std.getopt : getopt, GetOptException;
   import std.stdio : stdout, stderr;
   import mlib.term;
   import pd.pixiv;

   /* following (--private | --public) */
   if (args.length < 2) {
      stderr.writeln("pixiv_down: Must provide one of --public or --private.");
      stderr.writeln("Run 'pixiv_down following --help' for more information.");
      return 1;
   }

   bool fromPrivate = false;
   bool fromPublic = false;
   int skip;

   try {
      auto helpInformation = getopt(args,
         "private", &fromPrivate,
         "public", &fromPublic,
         "skip|s", &skip);

      if (helpInformation.helpWanted) {
         displayFollowingHelp();
         return 0;
      }
   } catch (GetOptException e) {
      stderr.writefln("pixiv_down following: %s", e.msg);
      stderr.writefln("Run 'pixiv_down help following' for more information.");
   }

   if (fromPublic && fromPrivate) {
      stderr.writefln("pixiv_down: Cannot download both publicly and privately followed accounts.");
      displayFollowingHelp();
      return 1;
   }

   if (!fromPublic && !fromPrivate) {
      stderr.writeln("pixiv_down: Must provide one of --public or --private.");
      stderr.writeln("Run 'pixiv_down following --help' for more information.");
      return 1;
   }

   const visibility = fromPrivate ? "privately" : "publicly";
   infof("Downloading %s followed accounts", visibility);

   stdout.writefln("Fetching %s followed accounts...", visibility);
   string[] userIds = fetchFollowingIDs(fromPrivate, skip, config);
   const total = userIds.length;
   infof("following: received total of %d accounts", total);
   Term.goUpAndClearLine(1);
   stdout.writefln("Fetched %s followed accounts.", visibility);


   foreach(index, id; userIds) {
      import app.cmds.artist;
      artistHandle(["artist", id], config);
   }

   return 0;
}

public void displayFollowingHelp()
{
   import std.stdio : stderr;

   stderr.writefln(
      "pixiv_down following - Bulk download all creators you follow.\n" ~
      "\nUsage:\tpixiv_down following [--public|--private] [options]\n" ~
      "\nDownload all content from every creator that you follow, starting\n" ~
      "from your most recently followed creator down to your least recently\n" ~
      "followed creator.\n" ~
      "\nYou must specify whether to download your publicaly or privately\n" ~
      "followed creators by using the --public and --private options. They\n" ~
      "cannot be combined.\n" ~
      "\nOptions:\n" ~
      "   -h, --help       \tPrint this help message and exit.\n" ~
      "   -s, --skip NUMBER\tSkip downloading the first NUMBER creators.\n" ~
      "   --private        \tDownload from privately followed creators.\n" ~
      "   --public         \tDownload from publicaly followed creators.\n" ~
      "\nExamples:\n" ~
      "\n  Download all publicaly followed accounts:\n" ~
      "       pixiv_down following --public\n" ~
      "\n  Download all privately followed accounts, skipping the first 2:\n" ~
      "       pixiv_down following --private --skip 2\n");
}

private:

string[] fetchFollowingIDs(bool private_, in int skip, const ref Config config)
{
   import std.array : array;
   import std.algorithm.iteration : map;
   import mlib.term;
   import pd.pixiv;
   import app.util : sleep;

   User[] users;
   long total;
   long offset = skip;

   do {
      User[] page = fetchFollowing(private_, offset, total, config);
      tracef("following page %s", page);
      users ~= page;
      offset += page.length;
      reportProgress(total - skip, offset - skip);
      sleep(1, 2, false);
   } while (offset < total);

   Term.clearCurrentLine();

   return cast(string[])users.map!(u => u.id).array;
}

void reportProgress(long total, long current)
{
   import std.format: format;
   import std.stdio;
   import mlib.term;

   Term.clearCurrentLine();

   const ratioCompleted = cast(double)current / total;
   const prefix = format!"%d ["(current);
   const suffix = format!"] %3.0f%% "(ratioCompleted * 100);
   const barLength = Term.getColumnCount() - prefix.length - suffix.length;
   stdout.write(prefix);

   foreach(i; 0..barLength) {
      stdout.write(i < (ratioCompleted * barLength) ? '#' : ' ');
   }
   stdout.write(suffix);

   stdout.flush();
}
